TypeScript 61.9%
HTML 37.2%
SQL 0.7%
1import { NextResponse } from 'next/server';2import { getCurrentUser } from '@/lib/auth/session';3import { getCollectionDetail } from '@/lib/account/queries';4import { toCsv } from '@/lib/account/portfolio';56export const dynamic = 'force-dynamic';78const COLUMNS = ['item_id', 'asset_id', 'asset_slug', 'title', 'category', 'variant', 'grader', 'grade', 'cert', 'serial', 'quantity', 'acquired_at', 'purchase_price', 'currency', 'purchase_price_usd', 'source', 'condition', 'value_usd', 'value_source', 'confidence', 'gain_usd', 'gain_pct', 'tags', 'notes'];910export async function GET(req: Request, ctx: { params: Promise<{ id: string }> }) {11 const u = await getCurrentUser();12 if (!u) return NextResponse.json({ error: 'unauthorized' }, { status: 401 });13 const { id } = await ctx.params;14 const detail = await getCollectionDetail(u.id, id);15 if (!detail) return NextResponse.json({ error: 'not found' }, { status: 404 });16 const format = new URL(req.url).searchParams.get('format') ?? 'csv';17 const rows = detail.summary.items.map((i) => {18 const src = detail.items.find((x) => x.id === i.id)!;19 return {20 item_id: i.id,21 asset_id: i.assetId,22 asset_slug: src.assetSlug,23 title: i.title,24 category: i.categorySlug,25 variant: src.variantLabel,26 grader: i.grader,27 grade: i.grade,28 cert: src.certificationNumber,29 serial: src.serial,30 quantity: i.quantity,31 acquired_at: i.acquiredAt,32 purchase_price: src.purchasePriceNative,33 currency: src.acquiredCurrency,34 purchase_price_usd: i.purchasePriceUsd,35 source: src.source,36 condition: src.condition,37 value_usd: i.valueUsd,38 value_source: i.valueSource,39 confidence: i.confidence,40 gain_usd: i.gainUsd,41 gain_pct: i.gainPct,42 tags: src.tags.join('|'),43 notes: src.notes,44 };45 });46 const stamp = new Date().toISOString().slice(0, 10);47 if (format === 'json') {48 return new NextResponse(JSON.stringify({ collection: detail.collection.name, exportedAt: new Date().toISOString(), summary: { valueUsd: detail.summary.valueUsd, costBasisUsd: detail.summary.costBasisUsd, gainUsd: detail.summary.gainUsd, returnPct: detail.summary.returnPct, items: detail.summary.itemCount }, items: rows }, null, 2), { headers: { 'content-type': 'application/json', 'content-disposition': `attachment; filename="rareindex-${detail.collection.publicSlug ?? id}-${stamp}.json"` } });49 }50 return new NextResponse(toCsv(rows, COLUMNS), { headers: { 'content-type': 'text/csv; charset=utf-8', 'content-disposition': `attachment; filename="rareindex-${detail.collection.publicSlug ?? id}-${stamp}.csv"` } });51}52